package ControllerLayer.ControlServices.Segmenter;

import DataLayer.ImageDataContainer.ImageComponentsInfo.IImageComponentsInfo;
import DataLayer.ImageDataContainer.ImageInfo.IImageInfo;
import java.awt.Point;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;


public class CharacterSegmenter implements ISegmentUpdater{
	private BufferedImage img;
        private int[] hist;

        // egyetlen szo betuinek a koordinatait fogja tartalmazni:
        private ArrayList<Point> charCoords;

        private ArrayList<Integer> localMins;
        // szavankent a bentuk koordinatait fogja tartalmazni;
        // egy ArrayList<Point> - ban lesz egyetlen szo betuinek a koordinatai
        private ArrayList<ArrayList<Point>> charAll;

	public CharacterSegmenter()
        {
        }

	public void segmentChars(IImageComponentsInfo imgCompInfo, IImageInfo imageInfo){
            img = imageInfo.getBWImage();

            charAll = new ArrayList<ArrayList<Point>>();
                
            ArrayList<Line2D> words = imgCompInfo.getWords();

            for(Line2D word : words){
                ////System.out.println(this.getClass() + " szegmentalom ezt a szot: "+word.getX1()+" "+word.getX2()+" "+word.getY1()+" "+word.getY2());
                segmentChar(word);
            }

	}

        /**
         * segmentates the  given word to characters;
         * @param word data structure containing the word's coordinates;
         * (Line2D.x1, Line2D.y1) represent the upper-left corner, while
         * (Line2D.x2, Line2D.y2) the lower-right corner of the word to segmentate
         */
        private void segmentChar(Line2D word){
            makeHistogram(word);
            charCoords = new  ArrayList<Point>();
            ////System.out.println("az iras atlagvastagsaga: " + findEpsilon());
            findMin((int) (findEpsilon() * 1));
            minimumProcess();
            makePoints();

//            System.out.println("Ennyi szot talaltam: "+charCoords.size());
//            for(int i = 0; i<charCoords.size(); ++i)
//                System.out.println(charCoords.get(i));
            
            charAll.add(charCoords);

        }

        
         /**
         * makes the column-based histogram of the word with the coordinated given
         * in p
         * @param p the coordinates of the word to segmentate
         */
        private void makeHistogram(Line2D p){
            //int height = img.getWidth();
            int height = (int) (p.getY2() - p.getY1() - 1);
          //  System.out.println("height: "+height);
            int width = (int) (p.getX2() - p.getX1() - 1);
          //  System.out.println("width: "+width);
            hist = new int[height];

            // counts the black pixels in each row
            for (int i = (int) (p.getY1() + 1); i<p.getY2(); ++i){
                int sum = 0;
                for(int j = (int) (p.getX1() + 1); j<p.getX2(); ++j){
                    int c = img.getRGB(i, j);
                    // condition based on which we count the pixels
                    if(c != -1)
                        ++sum;

                }
                int index = (int) (i - p.getY1() - 1);
                hist[index] = sum;
            }
        }

        private void findMin(int epsilon){

        int i = 0;
        int j = 1;

        int n = hist.length;
       // System.out.println("hossz: "+n);

        localMins = new ArrayList<Integer>();

        while(j<n){  // we check each row

            boolean found = false;

            // we search for the start of a row: __/
            while((j<n)&&(hist[i]==hist[j])){
                ++i;
                ++j;
                found = true;
            }

            if(found)
                localMins.add(i);

            // find a local maximum:
            while((j<n)&&(hist[i]<=hist[j])){
                ++i;
                ++j;
            }

            // find a local minimum:
            while((j<n)&&(hist[i]>hist[j])){
                ++i;
                ++j;
            }

            localMins.add(i);
        }


        ArrayList<Integer> temp = new ArrayList<Integer>();

        // a temporaly copy of the minimum points having their value less
        // than epsilon
        
        temp.add(localMins.get(0));  // this is needed if the writing starts exactly
                                     //   at the beginning of the row

        for(int row:localMins)
            if(hist[row]<epsilon)
                temp.add(row);

        temp.add(localMins.get(localMins.size()-1));  // this is needed if the writing ends exactly
                                                      //   at the end of the row

        // copy the final result back to localMins
        localMins.clear();
        for(int row:temp)
            localMins.add(row);
//
//        for(int row:localMins)
//                System.out.println(row);
        }

         /**
         * calculates the sum of the image histogram on the given interval
         * @param a beginning of the interval
         * @param b end of the interval
         * @return sum of the histogram
         */
        private int histSum(int a, int b){
            int sum = 0;
            for(int i = a; i < b; ++i)
                sum = sum + hist[i];
            return sum;
        }

        /**
         * calculates the average of the colums which lie between the characters
         * we assume that it is at leasr 40 characters wide
         * @return the average above mentioned
         */
        private double findEpsilon(){
            int sum=0;
            double count = 0;

            for(int i = 0; i<hist.length-1; ++i){
                if((hist[i]!=hist[i+1]) && (hist[i] < 20)){
                    ++ count;
                    sum = sum + hist[i];
                }
            }

            ////System.out.println("Atlagos iras vastagsaga: "+sum/count);
            return sum / count;
        }

        /**
         * counts the average width of a character - we assume that a character
         * is at leat 20 pixels wide
         * @return the average width of a character
         */
        private double avg()
        {
            int sum=0;
            double count = 0;
            int last = 0;
            for(int i = 1; i<localMins.size(); ++i){
                if(localMins.get(i)-localMins.get(last)>20){
                    ++ count;
                    sum = sum + localMins.get(i)-localMins.get(last);
                    last = i;
                }
            }

            ////System.out.println("Atlagos karakter: "+sum/count);
            return sum / count;
        }

        /**
         * removes the characters which are not wide enough
         */
        private void minimumProcess()
        {
              ArrayList<Integer> temp = new ArrayList<Integer>();
              for(int i = 0; i< localMins.size(); ++i)
                  temp.add(localMins.get(i));
              
              // getting the average width os a character:
              double avgchr = avg();
              
              localMins.clear();
              
              localMins.add(temp.get(0)); // putting the first character by default
              int last=0; // the last added element
              
              for (int j=1; j<temp.size(); ++j)
                  if (temp.get(j) - temp.get(last) > avgchr * 1 )
                  {
                      localMins.add(temp.get(j));
                      last=j;
                  }
        }

        /**
         * makes an array list of point out of the array list of integers
         */
        private void makePoints(){
            charCoords.clear();

            ////System.out.println("makePoints-ban a charCoords hossza: " + charCoords.size());

            int n = localMins.size();
            for(int i = 0; i<n-1; ++i){
                Point p = new Point(localMins.get(i), localMins.get(i+1));
                    charCoords.add(p);
            }
        }


	public void updateImageCompInfo(IImageComponentsInfo imgCompInfo)
        {
		imgCompInfo.addCharacterCoords(charAll);
	}
}
